// -*-mode:c++; coding:utf-8-*-

#ifndef _KB_BINARY_IARCHIVE_
#define _KB_BINARY_IARCHIVE_

#include <unistd.h>

#include <string>
#include <vector>

#ifndef NBOOST
#	include <boost/asio/buffer.hpp>
#endif	// NBOOST

#include "byteorder.hpp"
#include "detail/binary_archive_base.hpp"

namespace kb
{
#ifdef NDEBUG
	template<typename Byteorder,
		 typename Config = details::binary_archive_config_default>
#else
	template<typename Byteorder,
		 typename Config = details::binary_archive_config_debug> // debug feature enabled
#endif	// NDEBUG
	class binary_iarchive
		: public details::binary_archive_base<Config>
	{
	public:
		typedef details::binary_archive_base<Config> base_type;
		typedef binary_iarchive<Byteorder, Config> this_type;
		typedef Byteorder byteorder;

		using base_type::fail;
		using base_type::good;
		using base_type::clear_status;

	public:
		binary_iarchive()
			: m_data(NULL)
			, m_end(NULL)
			, m_pos(NULL) {}
		
		binary_iarchive(const char *data, size_t size)
			: m_data(data)
			, m_end(data + size)
			, m_pos(data) {}

		binary_iarchive(const char *begin, const char *end)
			: m_data(begin)
			, m_end(end)
			, m_pos(begin) {}

#ifndef NBOOST
		explicit
		binary_iarchive(const boost::asio::const_buffer &data)
			: m_data(boost::asio::buffer_cast<const char*>(data))
			, m_end(boost::asio::buffer_cast<const char*>(data)
				+ boost::asio::buffer_size(data))
			, m_pos(boost::asio::buffer_cast<const char*>(data)) {}
#endif	// NBOOST

		template<typename T>
		inline
		this_type &operator>>(T &obj) {
			load(&obj);
			return *this;
		}

		template<typename T>
		inline
		bool load(T *obj) {
			return details::binary_iarchive_helper<this_type, T>::load(*this, obj);
		}

		template<typename T>
		inline
		bool load_object(T *obj) {
			return obj->load(*this);
		}

		template<typename Integer>
		inline
		bool load_integer(Integer *n) {
			if(fail())
			{
				return false;
			}
			const char * const new_pos = m_pos + sizeof(Integer);
			if(base_type::check_data_bound(m_data, new_pos, m_end) == false)
			{
				return false;
			}
			const Integer * const p = reinterpret_cast<const Integer*>(m_pos);
			*n = byteorder_cast<byteorder, host_byteorder>(*p);
			m_pos = new_pos;
			return true;
		}

		template<typename CharT, typename Traits, typename Alloc>
		inline
		bool load_string(std::basic_string<CharT, Traits, Alloc> *str) {
			return basic_load_string<uint32_t>(str);
		}

		template<typename CharT, typename Traits, typename Alloc>
		inline
		bool load_string64(std::basic_string<CharT, Traits, Alloc> *str) {
			return basic_load_string<uint64_t>(str);
		}

		template<typename SizeT, typename CharT, typename Traits, typename Alloc>
		inline
		bool basic_load_string(std::basic_string<CharT, Traits, Alloc> *str) {
			typedef std::basic_string<CharT, Traits, Alloc> string_type;
			typedef typename string_type::value_type char_type;

			SizeT size = 0;
			if(load_integer(&size) == false)
			{
				return false;
			}
			if(base_type::template check_size_limits<std::size_t, SizeT>(size) == false)
			{
				return false;
			}
			const char * const new_pos = m_pos + size * sizeof(char_type);
			if(base_type::check_data_bound(m_data, new_pos, m_end) == false)
			{
				return false;
			}
			str->assign(static_cast<const char_type *>(m_pos),
				    static_cast<const char_type *>(new_pos));
			m_pos = new_pos;
			return true;
		}

		template<typename T, typename Alloc>
		inline
		bool load_vector(std::vector<T, Alloc> *vec) {
			return basic_load_vector<uint32_t>(vec);
		}

		template<typename T, typename Alloc>
		inline
		bool load_vector64(std::vector<T, Alloc> *vec) {
			return basic_load_vector<uint64_t>(vec);
		}

		template<typename SizeT, typename T, typename Alloc>
		inline
		bool basic_load_vector(std::vector<T, Alloc> *vec) {
			typedef std::vector<T, Alloc> vector_type;
			typedef typename vector_type::value_type value_type;

			SizeT size = 0;
			if(load_integer(&size) == false)
			{
				return false;
			}
			if(base_type::template check_size_limits<std::size_t, SizeT>(size) == false)
			{
				return false;
			}
			vec->clear();
			vec->reserve(size);
			value_type value; // 这里会调用默认构造函数
			for(SizeT i = 0; i < size; ++i)
			{
				if(load(&value) == false)
				{
					return false;
				}
				vec->push_back(value); // 这里会调用复制构造函数
			}
			return true;
		}

		inline
		bool seek(ssize_t pos, int whence) {
			const char * const base =
				((whence == SEEK_SET) ? m_data :
				 ((whence == SEEK_CUR) ? m_pos :
				  ((whence == SEEK_END) ? m_end : NULL)));
			if(base == NULL)
			{
				base_type::set_status(base_type::S_ERROR_BAD_SEEK_TYPE);
				return false;
			}
			const char * const new_pos = base + pos;
			if(base_type::check_data_bound(m_data, new_pos, m_end) == false)
			{
				return false;
			}
			m_pos = new_pos;
			return true;
		}

		bool empty() const
			{ return m_pos == m_end; }

		const char * left_buffer() const
			{ return m_pos; }

		std::size_t left_buffer_size() const
			{ return std::size_t(m_end - m_pos); }

		const char * used_buffer() const
			{ return m_data; }

		std::size_t used_buffer_size() const
			{ return std::size_t(m_pos - m_data); }

	private:
		const char * const m_data;
		const char * const m_end;
		const char * m_pos;
	};

} // namespace kb

#endif	// _KB_BINARY_IARCHIVE_
